// persistentq.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <queue>
#include <iostream>
#include <map>

using namespace std;
const int nBuffSize = 65536;
const int nMaxMappedFileSize =  0x200000;
const int nMaxChunckCount = 512;

struct Data
{
	char acData[50];
};


class PersistentQ
{
public:
	
	PersistentQ();
	
	~PersistentQ();
	
	//Get the front data from  queue and on success return true;
	bool GetFront(Data&);	
	//put  data  on the back of the queue and retrun true on success
	
	bool PushBack(Data&);
	//get current entry count
	int size();
	
	//restore queue data from last state and init the queue
	bool init(TCHAR *);
	//close file handlers and finish memory maping 
	void close();
private:

	//allocate block in the mapped file
	LPVOID GetBlock(size_t );

	//deallocate block in the mapped file
	void ReleaseBlock(LPVOID );


	HANDLE OpenOrCreateFile(LPCWSTR);

	PersistentQ(const PersistentQ&);
	PersistentQ operator = (const PersistentQ&);
	
	//memory mapped file handle
	HANDLE		m_hMapFile;
	
	//mapped address start
	LPCTSTR		m_pBuf;

	//file name =>path
	LPCTSTR		m_lpFileName;
	//Hard dsik file handle
	HANDLE		m_hFile;
	//Memory chunck map
	map<LPVOID,bool >	m_memorymap;//map of memory chunck.true for is memory in use.false for unused.
	// data queue
	queue<LPVOID>		m_stlDataQ;
};

PersistentQ::PersistentQ():m_hMapFile(INVALID_HANDLE_VALUE),m_pBuf(NULL),m_lpFileName(NULL),m_hFile(INVALID_HANDLE_VALUE)
{

}
PersistentQ::~PersistentQ()
{
	//cout << __FUNCTION__<<endl;
	close();
}
void PersistentQ::close()
{
	if (NULL != m_pBuf)
	{
		UnmapViewOfFile(m_pBuf);
	}
	if (INVALID_HANDLE_VALUE !=m_hMapFile)
	{
		CloseHandle(m_hMapFile); // close the file mapping object
	}
	if (INVALID_HANDLE_VALUE !=m_hFile)
	{
		CloseHandle(m_hFile); // close the file mapping object
	}
	if((NULL != m_lpFileName) && (0 == m_stlDataQ.size ()))
	{
		int iRet = DeleteFile(m_lpFileName);
		if (iRet>0)
		{
			cout<<"All data processed.Removed map file." <<endl;
		}
	}
}

HANDLE PersistentQ::OpenOrCreateFile(LPCWSTR pcFileName )
{
	HANDLE hFile = CreateFile(pcFileName,
		GENERIC_READ | GENERIC_WRITE,
		0,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);
	if (INVALID_HANDLE_VALUE != hFile)
	{
		cout<<"Opening existing mapped file."<<endl;
		return hFile;
	}
	else if (INVALID_HANDLE_VALUE == hFile)
	{
		cout<<"Mapped file not present." <<endl;
		hFile = CreateFile(pcFileName,
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			CREATE_NEW,
			FILE_ATTRIBUTE_NORMAL,
			NULL);

		if (INVALID_HANDLE_VALUE != hFile)
		{
			cout<<"Mapped file created." <<endl;
		}
		return hFile;
	}
	return hFile;
}

bool PersistentQ::init(TCHAR * pcFileName)
{
	bool bStat = false;
	do
	{
		HANDLE hFile = OpenOrCreateFile((LPCWSTR)pcFileName);
		if (INVALID_HANDLE_VALUE == hFile )
		{
			break;
		}
		m_hFile = hFile;
		m_lpFileName = pcFileName;
		m_hMapFile = CreateFileMapping( m_hFile,
			NULL, // default security
			PAGE_READWRITE, // read/write permission
			0, // size of mapping object, high
			nMaxMappedFileSize, // size of mapping object, low
			NULL); // name of mapping object

		if (m_hMapFile == NULL)
		{
			break;
		}
		else
		{
			cout<< "File mapping object successfully created."<<endl;
		}
		m_pBuf = (LPTSTR) MapViewOfFile(m_hMapFile, // handle to map object
			FILE_MAP_ALL_ACCESS, // read/write permission
			0,
			0,
			nBuffSize);

		if (m_pBuf == NULL)
		{
			break;
		}
		const int nChunkSize = sizeof(Data);
		Data osData;
		memset(&osData,0,sizeof(Data));

		for (int i = 0;i<nMaxChunckCount;i++)
		{
			bool bContainsData = false;
			LPVOID addr = (LPVOID) (m_pBuf + i*nChunkSize);
			if (0 != memcmp(addr, &osData, sizeof(Data) ) )
			{
				bContainsData = true;				
				m_stlDataQ.push(addr);
			}
			m_memorymap[addr] = bContainsData;
		}
		bStat = true;
	} while(0);
	return bStat;
}

LPVOID PersistentQ:: GetBlock(size_t nReQSize )
{
	map<LPVOID,bool >::iterator it = m_memorymap.begin();
	LPVOID vRet = NULL;
	while(it!= m_memorymap.end())
	{
		if (it->second == false)
		{
			m_memorymap[it->first] = true;
			vRet = it->first;			
			break;
		}
		it++;
	}
	return vRet;
}

bool PersistentQ::PushBack(Data& dt)
{
	bool bStatus = false;
	LPVOID pdt = GetBlock(sizeof(Data));
	if (NULL!=pdt)
	{
		memcpy(pdt,&dt,sizeof(Data));
		m_stlDataQ.push(pdt);
		bStatus = true;
	}
	else
	{
		cout<<"queue is full.No more insert possible without de-queueing data."<<endl;
	}
	return bStatus;
}
bool PersistentQ::GetFront(Data& dt )
{
	bool bStatus = false;
	Data* pdt = NULL;
	if (m_stlDataQ.size() >0)
	{
		pdt = (Data*)m_stlDataQ.front();
		m_stlDataQ.pop();
		if (NULL== pdt)
		{
			return false;
		}
		memcpy(&dt,pdt,sizeof(Data));
		ReleaseBlock((LPVOID)pdt);
		bStatus = true;
	}
	else
	{
		cout<<"queue is empty."<<endl;
	}
	return bStatus;
}

int PersistentQ::size()
{
	return m_stlDataQ.size();
}

void PersistentQ::ReleaseBlock(LPVOID pvData)
{
	if ( true== m_memorymap[pvData])
	{
		m_memorymap[pvData] = false;		
		memset(pvData,0,sizeof(Data));
	}
}

BOOL CtrlHandler( DWORD fdwCtrlType );
PersistentQ gPQ;
int _tmain(int argc, _TCHAR* argv[])
{
	if( SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE ) )
	{
		cout<<"The Control Handler is installed for Ctrl+C or Ctrl+Break or logging off or Shudown." <<endl;
	}

	TCHAR * lpcTheFile = TEXT("mapfile.bin"); // file for q data

	bool bInitStat = gPQ.init(lpcTheFile);
	if ( true == bInitStat)
	{		
		bool bPopStatus =  false;
		const int nRuncnt = 5;
		for( int i = 0;i<gPQ.size() ;i++)
		{
			Data dt;
			bPopStatus = gPQ.GetFront(dt);
			if (true == bPopStatus)
			{
				cout<<"popded data " << dt.acData <<endl;
			}
		}
		for( int i = 0;i<nRuncnt;i++)
		{
			SYSTEMTIME st;
			GetLocalTime(&st);
			Data dt;
			memset(&dt,0,sizeof(Data));
			sprintf_s(dt.acData,"Data instance with value %d time is: %02d:%02d:%02d\n",i,st.wHour, st.wMinute,st.wSecond);
			gPQ.PushBack(dt);
			//terminate();
		}
		for( int i = 0;i<nRuncnt -2 ;i++)
		{
			Data dt;
			bool bPopStatus = gPQ.GetFront(dt);
			if (true == bPopStatus)
			{
				cout<<"popded data " << dt.acData <<endl;
			}
		}
	}
	return 0;
}

BOOL CtrlHandler( DWORD fdwCtrlType )
{
	switch( fdwCtrlType )
	{
	case CTRL_C_EVENT:
	case CTRL_CLOSE_EVENT:
	case CTRL_BREAK_EVENT:
	case CTRL_LOGOFF_EVENT:
	case CTRL_SHUTDOWN_EVENT:
		cout<<"Entered into " <<__FUNCTION__ <<"Event type "<<fdwCtrlType<<" . Closing data container."<<endl;
		gPQ.close();
		return( TRUE );
	default:
		return FALSE;
	}
}